package engine;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Vector;
import java.util.concurrent.ConcurrentHashMap;

import javax.servlet.ServletContext;

import dao.DaoGare;
import externaldata.gare.Gare;
import externaldata.passageTrainTheorique.PassageTrainTheoriqueLive;


/**
 * Thread interne à l'application dont le rôle sera : 
 * 		1/ Checker régulièrement les données des trains via l'API SNCF
 * 		2/ Mettre à jour les paris des joueurs dans la BDD en fonction des données récupérées
 */
public class BackgroundTaskEngine implements Runnable {
	
	private List<Gare> garesAVerifier;
	private Thread weeklyUpdaterThread;
	private List<Thread> stationTasksRunning;
	private boolean allThreadsInterrupted;
	private ServletContext context;
	
	public BackgroundTaskEngine(ServletContext context) {
		
		stationTasksRunning = new ArrayList<Thread>();
		allThreadsInterrupted = false;
		this.context = context;
		
		garesAVerifier = DaoGare.getAllGaresWithDua();
		
		
		/* Initialisation des données du context ---------------------------- */
		Map<String, List<PassageTrainTheoriqueLive>> paris_disponibles = 
				new ConcurrentHashMap<String, List<PassageTrainTheoriqueLive>>();
		
		for (Gare tmpGare : garesAVerifier)
    		paris_disponibles.put(tmpGare.getDua(), new Vector<PassageTrainTheoriqueLive>());
		
		context.setAttribute("paris_disponibles", paris_disponibles);
		/* ------------------------------- Fin de l'initialisation du context */
	}

	@Override
	public void run() {
		
		boolean threadPrincipalRunning = true;
		Object verrou = new Object();
		
		
		synchronized (verrou) {
		
			// Lancement du thread chargé de récupérer le fichier d'horaires théoriques
			// renouvelé chaque semaine
			weeklyUpdaterThread = new Thread(new DailyDatabaseUpdater(garesAVerifier, verrou));
			weeklyUpdaterThread.start();
			
			// On attend que la weeklyUpdaterThread finisse de récupérer les premières données
			try {
				verrou.wait();
			} catch (InterruptedException e) {
				interruptAllThreads();
				joinAllThreads();
				
				// On termine l'exécution du thread en ne rentrant pas dans la boucle suivante
				threadPrincipalRunning = false;
			}
		
		}
		
		
		while (threadPrincipalRunning) {
			
			try {

				cleanStationTasksRunning();
				
				for (int i = 0; i < garesAVerifier.size(); i++) {
					Thread stationTask = new Thread(new StationTask(garesAVerifier.get(i), context));
					stationTask.start();
					
					stationTasksRunning.add(stationTask);
				}
				
				// Nouvelle vérification des temps réels toutes les 35 secondes
				Thread.sleep(35000);
				
			} catch (InterruptedException e) {
				interruptAllThreads();
				joinAllThreads();
				
				// On termine l'exécution du thread en sortant de la boucle
				threadPrincipalRunning = false;
			}
		}
		
		System.out.println("Fin du BackgroundTaskEngine ...");
	}

	
	
	
	
	
	// ------------------------ METHODES DE GESTION DES THREADS --------------------------------------
	
	
	/**
	 * Supprime tous les threads qui ont fini leur exécution de la liste des stationTasksRunning
	 */
	private void cleanStationTasksRunning() {
		
		List<Thread> tmpStationTasks = new ArrayList<Thread>(stationTasksRunning);
		
		for (Thread tmpThread : stationTasksRunning) {
			if (!tmpThread.isAlive()) {
				tmpStationTasks.remove(tmpThread);
			}
		}
		
		stationTasksRunning = tmpStationTasks;
	}
	
	
	/**
	 * Lance une interruption sur chaque le weeklyUpdaterThread et sur les stationTasks encore vivants
	 */
	private void interruptAllThreads() {
		
		weeklyUpdaterThread.interrupt();
		
		for (Thread tmpThread : stationTasksRunning) {
			if (tmpThread.isAlive())
				tmpThread.interrupt();
		}
		
		allThreadsInterrupted = true;
	}
	
	
	/**
	 * Attend la fin de l'exécution du weeklyUpdateThread et des stationTasks encore vivants
	 */
	private void joinAllThreads() {
		
		if (allThreadsInterrupted) {
			try {
				weeklyUpdaterThread.join();
				
				for (Thread tmpThread : stationTasksRunning) {
					tmpThread.join();
				}
				
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	}
}
